package com.example.tsbs.util;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.core.DockerClientBuilder;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;

@Slf4j
public class FileUtil {

    /**
     * 检验文件后缀名
     * @param filePath     文件路径（包括文件名）/root/t.txt
     * @param suffixType   文件后缀名txt
     * @return
     */
    public static boolean checkFileType(String filePath, String suffixType) {
        String extension = filePath.substring(filePath.lastIndexOf(".") + 1);
        if (extension.equals(suffixType)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 删除文件
     */
    public static void deleteFiles(String[] filePath) {
        StringBuilder result = new StringBuilder();
        for (String path : filePath) {
            // 创建File对象，指向要删除的文件
            File file = new File(path);
            // 调用delete方法尝试删除文件
            if (file.delete()) {
                result.append(filePath).append("文件删除成功\t");
            } else {
                result.append(filePath).append("文件删除失败\t");
            }
        }
        log.info(result.toString());
    }

    /**
     * 文件重命名
     * @param oldFilePath
     * @return
     */
    public static String renameFile(String oldFilePath) {
        String newPath = "";
        // 创建File对象指向旧文件
        File oldFile = new File(oldFilePath);
        // 创建File对象指向新文件名
        File newFile = new File(toPinyin(oldFilePath));
        // 使用renameTo方法进行重命名
        boolean renamed = oldFile.renameTo(newFile);

        // 输出结果
        if (renamed) {
            log.info("文件重命名成功！");
            newPath = newFile.getAbsolutePath();
            return newPath;
        } else {
            log.info("文件重命名失败，确保文件未被打开且路径正确。");
            return "文件重命名失败，确保文件未被打开且路径正确";
        }
    }

    /**
     * 检测文件后缀是否为XXX   如：dat
     * @param filePath 文件路径
     * @return
     */
    public static boolean checkFileExtension(String filePath, String suffixName) {
        // 使用File.separator获取正确的路径分隔符
        String extension = "";
        int i = filePath.lastIndexOf(File.separator);
        if (i > 0) {
            filePath = filePath.substring(i + 1);
        }
        i = filePath.lastIndexOf('.');
        if (i > 0) {
            extension = filePath.substring(i + 1);
        }
        return suffixName.equalsIgnoreCase(extension);
    }

    /**
     * 检测docker容器中是否存在某文件
     * @param containerId docker容器id
     * @param path  文件路径（包括文件名）
     * @return
     */
    public static boolean checkDockerFile(String containerId, String path) {
        try (DockerClient dockerClient = DockerClientBuilder.getInstance().build()) {
            String command = containerId + " ls -l " + path;
            String execCreateCmdResponse = String.valueOf(dockerClient.execCreateCmd(command).exec());
            if (StringUtils.isNotBlank(execCreateCmdResponse)) {
                // 文件或目录存在
                return true;
            }
        } catch (Exception e) {
            // 处理异常
            log.info("docker容器" + containerId + "中" + path + "不存在");
        }
        return false;
    }

    /**
     * 读取json文件内容并转换为json字符串
     * @param filePath 文件路径
     * @return
     **/
    public static String readFileToString(String filePath) {
        String jsonString = null;
        FileInputStream fileInputStream = null;

        try {
            fileInputStream = new FileInputStream(filePath);
            jsonString = IOUtils.toString(fileInputStream, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return jsonString;
    }

    /**
     * 拼接dat文件
     * @param filePath  {"D:/1/0.dat","D:/1/1.dat","D:/1/2.dat"};
     * @param outputFile "D:/1/result.dat"
     */
    public static String concatDatFiles(String[] filePath,String outputFile) {
        String result = "";
        // 文件列表
        List<FileInputStream> files = new ArrayList<>();

        // 结果dat文件不存在则创建文件
        FileUtil.isFileExistAndCreate(outputFile);

        // 添加dat文件到列表
        try {
            for (String file : filePath){
                if(isFileExist(file) == false) {
                    return file+"文件不存在";
                }
                files.add(new FileInputStream(file));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // 创建Enumeration
        Vector<FileInputStream> fileVector = new Vector<>(files);
        Enumeration<FileInputStream> e = fileVector.elements();

        try {
            // 创建SequenceInputStream
            SequenceInputStream sis = new SequenceInputStream(e);

            // 输出到文件
            FileOutputStream fos = new FileOutputStream(outputFile);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = sis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }

            // 关闭流
            sis.close();
            fos.close();
        } catch (IOException e1) {
            e1.printStackTrace();
            return "合并"+outputFile+"失败";
        }
        log.info("成功合并"+outputFile+"文件");
        result = "成功";
        return result;
    }


    /**
     * 确定文件是否存在  不存在则报错打印
     * @param filePath  文件路径（包括文件名）
     * @return
     */
    public static boolean isFileExist(String filePath) {
        Path path = Paths.get(filePath);
        boolean exists = Files.exists(path);
        if(exists == false) {
            log.info(filePath+"文件不存在");
        }
        return exists;
    }

    /**
     * 确定文件是否存在
     * @param filePath  文件路径（包括文件名）不存在,创建
     * @return
     */
    public static boolean isFileExistAndCreate(String filePath) {
        Path path = Paths.get(filePath);
        boolean exists = Files.exists(path);
        if(exists == false) {
            log.info(filePath+"文件不存在,创建");
            File file = new File(filePath);
        }
        return exists;
    }

    /**
     * 确定文件夹是否存在
     * @param filePath  文件路径（不包括文件名）不存在,创建
     * @return
     */
    public static boolean isFileFlowExistAndCreate(String filePath) {
        Path path = Paths.get(filePath);
        if(Files.exists(path) == false) {
            log.info(filePath+"文件夹不存在，创建");
            creatFileMkdir(filePath);
        }
        return true;
    }

    /**
     * 删除单个文件
     * @param filePath  "path/to/your/file1.txt"
     */
    public static void delectFile(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
            log.info("成功删除文件: " + file);
        }
    }

    /**
     * 删除多个文件
     * @param filePath  {"path/to/your/file1.txt","path/to/your/file2.txt","path/to/your/file3.txt"}
     */
    public static void delectFiles(String[] filePath) {
        List<File> filesToDelete = new ArrayList<>();
        for(String file : filePath) {
            filesToDelete.add(new File(file));
        }
        for (File file : filesToDelete) {
            if (file.delete()) {
                log.info("成功删除文件: " + file.getName());
            } else {
                log.info("删除文件失败: " + file.getName());
            }
        }
    }

    /**
     * 读取dat文件数据
     */
    public static String readDatFile(String filePath) {
        String result = null;
        File file = new File(filePath);
        if (!file.exists()) {
            return filePath+"文件不存在";
        }
        try {
            FileInputStream fileInputStream = new FileInputStream(filePath);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            // 读取整行数据
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }

            result = line;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 将字符串数据并写入二进制文件
     * @param filePath  D:/test/filetest/test.txt
     * @param content   "内容"
     */
    public static boolean writeStringToFile (String filePath, String content) {
        File file = new File(filePath);
        try {
            //判断是否存在文件，不存在则新建，若存在则覆盖
            if (!file.exists()) {
                file.createNewFile();
            } else {
                file.delete();
                file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            writer.write(content); // 将字符串写入文件
        } catch (IOException e) {
            e.printStackTrace(); // 处理异常
        }
        return true;
    }

    /**
     * 创建文件夹
     * @param fileMkdirPath
     * @return
     */
    public static boolean creatFileMkdir(String fileMkdirPath) {
        Path path = Paths.get(fileMkdirPath);
        try {
            path = Files.createDirectories(path);
            log.info(path+"文件夹创建成功！");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            log.info(path+"文件夹创建失败！");
            return false;
        }
    }

    /**
     * 将JSON字符串直接写入JSON文件。需要注意的是，这种方法会完全覆盖文件的内容。
     * @param jsonString     JSON字符串
     * @param jsonFileName   JSON文件名称  /root/a.json
     */
    public static boolean creatJsonFile(String jsonString,String jsonFileName) {
        log.info("开始生成json文件");
        isFileFlowExistAndCreate(getMkdirPath(jsonFileName));
        isFileExistAndCreate(jsonFileName);

//        try (FileWriter file = new FileWriter(jsonFileName)) {
//            file.write(jsonString);
//            file.flush();
//            log.info(jsonFileName+"文件生成成功");
//            return true;
//        } catch (IOException e) {
//            log.error(jsonFileName+"文件创建失败");
//            e.printStackTrace();
//            return false;
//        }

        ObjectMapper objectMapper = new ObjectMapper();
        try (FileWriter fileWriter = new FileWriter(jsonFileName)) {
            objectMapper.writerWithDefaultPrettyPrinter().writeValue(fileWriter, objectMapper.readTree(jsonString));
            log.info(jsonFileName+"文件生成成功");
            return true;
        } catch (IOException e) {
            log.error(jsonFileName+"文件创建失败");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将汉字转换为拼音
     * @param chinese
     * @return
     */
    public static String toPinyin(String chinese) {
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

        StringBuilder sb = new StringBuilder();
        char[] chars = chinese.toCharArray();
        for (char c : chars) {
            if (Character.isWhitespace(c)) {
                continue;
            }
            if (c >= '\u4e00' && c <= '\u9fa5') {
                try {
                    String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format);
                    sb.append(pinyinArray[0]);
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 复制文件到目标文件夹并且将中文转换成拼音
     * @param originalFilePath  /path/to/source/file.txt
     * @param targetFileMdkirPath    /path/to/destination文件夹
     * @return
     */
    public static String copyFileAndToPInYIn(String originalFilePath,String targetFileMdkirPath) {
        String res = "";
        // 源文件路径
        Path sourcePath = Paths.get(originalFilePath);
        // 目标文件夹路径
        Path destinationDirectory = Paths.get(targetFileMdkirPath);
        // 目标文件完整路径
        Path destinationPath = destinationDirectory.resolve(sourcePath.getFileName());
        //将中文转换成拼音
        destinationPath = Paths.get(toPinyin(destinationPath.toString()));
        // 如果目标文件已经存在，则删除它
        if(Files.exists(destinationPath)) {
            log.info("目标文件已经存在，开始删除");
            delectFile(destinationPath.toString());
        }
        try {
            // 复制文件到目标文件夹
            log.info("开始复制文件: "+sourcePath.toString()+"到"+destinationPath.toString());
            Files.copy(sourcePath, destinationPath);
            System.out.println("文件复制成功！");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件复制失败！");
        }
        return res;
    }

    /**
     * 复制文件到目标文件夹
     * @param originalFilePath  /path/to/source/file.txt
     * @param targetFileMkdirPath    /path/to/destination文件夹
     * @return
     */
    public static String copyFile(String originalFilePath,String targetFileMkdirPath) {
        String res = "";
        // 源文件路径
        Path sourcePath = Paths.get(originalFilePath);
        // 目标文件夹路径
        Path destinationDirectory = Paths.get(targetFileMkdirPath);
        // 目标文件完整路径
        Path destinationPath = destinationDirectory.resolve(sourcePath.getFileName());
        if(Files.exists(destinationPath)) {
            delectFile(destinationPath.toString());
        }
        try {
            // 复制文件到目标文件夹
            log.info("开始复制文件: "+sourcePath.toString()+"到"+destinationPath.toString());
//            Files.copy(sourcePath, destinationPath);
            Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
            res = "成功";
        } catch (IOException e) {
            e.printStackTrace();
            return "文件复制失败！";
        }
        return res;
    }

    /**
     * 获取文件夹路径
     * @param fileName  文件绝对路径（带文件名） /root/a.txt
     * @return /root
     */
    public static String getMkdirPath (String fileName) {
        String path = fileName;
        File file = new File(path);
        File directory = file.getParentFile();
        if (directory != null) {
            return directory.getPath();
        } else {
            log.info("No directory found."+fileName);
            return "";
        }
    }

    /**
     * 复制文件夹下所有文件到指定文件夹
     * @param originalFolderPath  源文件夹路径
     * @param targetFolderPath    目标文件夹路径
     * @return
     */
    public static String CopyFolder(String originalFolderPath,String targetFolderPath ) {
        String res = "";
        // 源文件夹路径
        //Path sourceFolder = Paths.get("/path/to/source/folder");
        Path sourceFolder = Paths.get(originalFolderPath);
        // 目标文件夹路径
        //Path targetFolder = Paths.get("/path/to/target/folder");
        Path targetFolder = Paths.get(targetFolderPath);

        try {
            //目标文件夹如果不存在则创建，存在则判断是否是目录
            File targetFile = file(targetFolderPath);
            if (targetFile.exists()){
                if (!targetFile.isDirectory()){
                    throw new FileSystemException("target not directory");
                }
            }else{
                targetFile.mkdirs();
            }
            // 使用Files.walk方法遍历源文件夹下的所有文件
            Files.walk(sourceFolder)
                    .filter(Files::isRegularFile) // 过滤出文件
                    .forEach(source -> {
                        try {
                            // 计算相对路径
                            Path relativePath = sourceFolder.relativize(source);
                            // 目标路径
                            Path targetPath = targetFolder.resolve(relativePath);
                            // 创建必要的中间文件夹
                            Files.createDirectories(targetPath.getParent());
                            // 复制文件
                            Files.copy(source, targetPath, StandardCopyOption.REPLACE_EXISTING);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
            //System.out.println("文件复制完成。");
            res = "成功";
        } catch (IOException e) {
            res = e.getCause().toString();
            e.printStackTrace();
        } finally {
            return res;
        }
    }

    /**
     * 存储上传的图片
     * @param originalPath 原始图片路径（包括文件名）
     * @param newPath 目标保存路径（包括文件名）
     * @return
     */
    public static boolean saveAsImgFile(String originalPath,String newPath) {
        try {
            FileInputStream fis = new FileInputStream(originalPath);
            FileOutputStream fos = new FileOutputStream(newPath);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            fis.close();
            fos.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            log.info("存储图片失败");
            return false;
        }
    }

    /**
     * 上传的文件，存储到本地目录
     * @param filePath 存储到本地的目录
     * @param file  文件
     */
    public static boolean createLocalFile(String filePath, MultipartFile file) {
        File localFile = new File(filePath);
        //如果不存在先创建目录
        if (!localFile.exists()) {
            localFile.mkdirs();
        }

        String originalFilename = file.getOriginalFilename();
        String path = filePath+"/"+originalFilename;

        log.info("createLocalFile path = {}", path);

        localFile = new File(path);
        FileOutputStream fos = null;
        InputStream in = null;
        try {
            if(localFile.exists()){
                //如果文件存在删除文件
                boolean delete = localFile.delete();
                if (delete == false){
                    log.error("Delete exist file \"{}\" failed!!!",path,new Exception("Delete exist file \""+path+"\" failed!!!"));
                }
            }
            //创建文件
            if(!localFile.exists()){
                //如果文件不存在，则创建新的文件
                localFile.createNewFile();
                log.info("Create file successfully,the file is {}",path);
            }

            //创建文件成功后，写入内容到文件里
            fos = new FileOutputStream(localFile);
            in = file.getInputStream();
            byte[] bytes = new byte[1024];

            int len = -1;

            while((len = in.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }

            fos.flush();
            //log.info("Reading uploaded file and buffering to local successfully!");
            log.info("成功读取上传的文件并缓冲到本地!");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }finally {
            try {
                if(fos != null) {
                    fos.close();
                }
                if(in != null) {
                    in.close();
                }
            } catch (IOException e) {
                //log.error("InputStream or OutputStream close error : {}", e);
                log.error("InputStream或OutputStream关闭错误 : {}", e);
                return false;
            }
        }

        return true;
    }

    /**
     * 上传多个文件，存储到本地
     * @param filePath 存储到本地的目录
     * @param files  多个文件
     */
    public static boolean createLocalFiles(String filePath, MultipartFile[] files) {
        File localFile = new File(filePath);
        //如果不存在先创建目录
        if (!localFile.exists()) {
            localFile.mkdirs();
        }

        for(MultipartFile file : files) {
            String originalFilename = file.getOriginalFilename();
            String path = filePath + "/" + originalFilename;

            log.info("本地的目录path = {}", path);

            localFile = new File(path);
            FileOutputStream fos = null;
            InputStream in = null;
            try {
                //创建文件
                if (!localFile.exists()) {
                    //如果文件不存在，则创建新的文件
                    localFile.createNewFile();
                    log.info("创建文件成功，path:{} ", path);
                }

                //创建文件成功后，写入内容到文件里
                fos = new FileOutputStream(localFile);
                in = file.getInputStream();
                byte[] bytes = new byte[1024];
                int len = -1;
                while ((len = in.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }

                fos.flush();
                log.info("成功读取上传的文件并缓冲到本地!");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return false;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            } finally {
                try {
                    if (fos != null) {
                        fos.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException e) {
                    log.error("InputStream或OutputStream关闭错误 : ", e);
                    return false;
                }
            }
        }

        return true;
    }



    /**
     * 创建一个File
     * @param path 路径
     */
    public static File file(String path){
        return new File(path);
    }

    /**
     * 递归查询文件
     * @param sourceDir 源文件
     * @param destDir 目标文件夹
     * @param names 匹配的后缀名
     */
    public static void copyFilesRecursion(File sourceDir,String destDir,String...names) throws IOException {
        // 遍历源文件夹下的所有文件和文件夹
        for (File file : sourceDir.listFiles()) {
            if (file.isDirectory()) { // 如果是文件夹，递归调用 copyImages 函数
                copyFilesRecursion(file, destDir,names);
            } else if (isMateFile(file,names)) { // 如果是图片文件，拷贝到目标文件夹中
                copy(file,destDir);
            }
        }
    }

    /**
     * 非递归查询文件
     * @param sourceDir 源文件
     * @param destDir 目标文件夹
     * @param names 匹配的后缀名
     */
    public static void copyFiles(File sourceDir,String destDir,String...names) throws IOException {
        // 遍历源文件夹下的所有文件和文件夹
        for (File file : sourceDir.listFiles()) {
            if (file.isFile() && isMateFile(sourceDir,names)) {
                copy(file,destDir);
            }
        }
    }

    /**
     * 拷贝文件到目标文件夹
     * @param sourceFile 源文件
     * @param destinationFolder 目标文件夹 (文件夹不存在则创建)
     */
    public static void copy(File sourceFile, String destinationFolder) throws IOException {
        String destinationFilePath = destinationFolder + "/" + getFileSimpleName(sourceFile);
        File file = new File(destinationFilePath);
        if (file.exists()){
            file.delete();
        } else {
            file.getParentFile().mkdirs();
        }
        Files.copy(sourceFile.toPath(), file.toPath());
    }

    /**
     * 判断文件名是否匹配
     * @param file 文件
     * @param names 需要匹配的后缀 png、doc、ppt
     * @return 如果没有填，则是文件就复制
     */
    public static boolean isMateFile(File file,String...names) {
        if (names == null || names.length == 0){
            return true;
        }
        return Arrays.asList(names).contains(getFileType(file).toLowerCase());
    }

    /**
     * 获取文件的后缀
     * @param file 文件  Z:\jiayi\postpartum\images\doduo.zip
     * @return zip
     */
    public static String getFileType(File file) {
        String fileName = file.getName();
        int dotIndex = fileName.lastIndexOf(".");
        return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
    }

    /**
     * 获取文件名
     * @param file  Z:\jiayi\postpartum\images\doduo.zip
     * @return doudou.zip
     */
    public static String getFileSimpleName(File file){
        return  file.getName().replaceAll("[\\\\/]", "");
    }

}
